﻿namespace Kodefu
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    public class StringGraph
    {
        private List<Node<string, int>> nodes = new List<Node<string,int>>();
        readonly Func<INode<string, int>, INode<string, int>, int> calculateWeight;

        public StringGraph()
        {
            calculateWeight = (node, newNode) =>
                {
                    int maxLength = node.Value.Length < newNode.Value.Length ?
                        node.Value.Length : newNode.Value.Length;
                    for (int i = node.Value.Length - maxLength; i < maxLength; i++)
                    {
                        if (node.Value.EndsWith(newNode.Value.Substring(0, maxLength - i)))
                        {
                            return maxLength - i;
                        }
                    }

                    return 0;
                };
        }

        public StringGraph(IEnumerable<string> strings) : this()
        {
            strings.Run(Add);
        }

        public IEnumerable<Node<string, int>> Nodes
        {
            get
            {
                return nodes;
            }
            private set
            {
                nodes = new List<Node<string, int>>(value);
            }
        }

        public void Add(string value)
        {
            if (!nodes.Exists(n => n.Value == value))
            {
                var newNode = new Node<string, int>(value)
                {
                    CalculateWeight = calculateWeight
                };

                nodes.Run(node =>
                    {
                        newNode.Add(node);
                        node.Add(newNode);
                    });

                nodes.Add(newNode);
            }
        }

        public StringGraph With(string value)
        {
            Add(value);
            return this;
        }

        public StringGraph Reduce()
        {
            if (nodes.Count <= 1)
            {
                return this;
            }

            var edges = from n in nodes
                        from e in n.Edges
                        select e;
            var edge = edges.First(n => n.Weight == edges.Max(e => e.Weight));                        
            
            return new StringGraph(nodes.Select(n => n.Value)
                                        .Where(str => str != edge.Parent.Value && str != edge.Node.Value)
                                        .With(edge.Combine().Value));
        }

        public string Compress()
        {
            StringGraph graph = this;
            while (graph.nodes.Count > 1)
            {
                graph = graph.Reduce();
            }

            return graph.nodes[0].Value;
        }      
    }
}
